<template>
  <div id="webgl"></div>
</template>

<script setup>
import * as THREE from "three";
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
//引入性能监视器stats.js
import Stats from "three/addons/libs/stats.module.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { onMounted, ref } from "vue";
import demo from "./demo.js";
import loader from "./loader";
const fps_value = ref(0);
const spt_value = ref(0);
onMounted(() => {
  // 创建3D场景对象Scene
  const scene = new THREE.Scene();
  //创建一个长方体几何对象Geometry
  let mesh = function () {
    // const material = new THREE.MeshPhongMaterial({
    //   color: 0xff0000, //0xff0000设置材质颜色为红色
    //   transparent: true, //开启透明
    //   opacity: 0.5, //设置透明度
    // });

    const material = new THREE.MeshPhongMaterial({
      color: 0xff0000,
      shininess: 20, //高光部分的亮度，默认30
      specular: 0x444444, //高光部分的颜色
    });
    const geometry = new THREE.BoxGeometry(100, 100, 100);
    const mesh2 = new THREE.Mesh(geometry, material); //网格模型对象Mesh
    // 相机位置xyz坐标：0,10,0
    //设置网格模型在三维空间中的位置坐标，默认是坐标原点
    mesh2.position.set(100, 100, 0);
    // scene.add(mesh2)
    return mesh2;
  };
  // 两个参数分别为几何体geometry、材质material
  // 实例化一个透视投影相机对象
  // width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
  let camera = function () {
    const width1 = 800; //宽度
    const height1 = 500; //高度
    const camera = new THREE.PerspectiveCamera(90, width1 / height1, 1, 3000);
    // 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000：远裁截面
    // const camera = new THREE.PerspectiveCamera(30, width1 / height1, 1, 3000);
    //相机在Three.js三维坐标系中的位置
    // 根据需要设置相机位置具体值
    camera.position.set(200, 200, 200);
    //相机观察目标指向Threejs 3D空间中某个位置
    camera.lookAt(0, 0, 0); //坐标原点
    camera.lookAt(0, 10, 0); //y轴上位置10
    camera.lookAt(mesh().position); //指向mesh对应的位置
    // 长方体尺寸100, 100, 100
    // 相机位置xyz坐标：200, 200, 200
    return camera;
  };

  let MeshLambertMaterial = function () {
    //点光源：两个参数分别表示光源颜色和光照强度
    // 参数1：0xffffff是纯白光,表示光源颜色
    // 参数2：1.0,表示光照强度，可以根据需要调整
    var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    // 设置光源的方向：通过光源position属性和目标指向对象的position属性计算
    directionalLight.position.set(80, 100, 50);
    // 方向光指向对象网格模型mesh，可以不设置，默认的位置是0,0,0
    directionalLight.target = mesh();
    scene.add(directionalLight);
    // 光源辅助观察
    const pointLightHelper = new THREE.PointLightHelper(directionalLight, 10);
    scene.add(pointLightHelper);
    // 链接：https://juejin.cn/post/7036927813168922638
  };

  function renderFn(scene, camera, mesh) {
    renderer.render(scene, camera); //执行渲染操作
    mesh.rotateY(0.01); //每次绕y轴旋转0.01弧度
    requestAnimationFrame(render); //请求再次执行渲染函数render，渲染下一帧
  }
  let render = function (width_value, height_value) {
    MeshLambertMaterial();
    const renderer = new THREE.WebGLRenderer({
      antialias: true,
    });
    // 定义threejs输出画布的尺寸(单位:像素px)
    const width = width_value || 800; //宽度
    const height = height_value || 500; //高度
    let camera_obj = camera();
    let mesh_obj = mesh();
    renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
    scene.add(mesh_obj);
    const controls = new OrbitControls(camera_obj, renderer.domElement);
    // 如果OrbitControls改变了相机参数，重新调用渲染器渲染三维场景
    controls.addEventListener("change", function () {
      // 浏览器控制台查看相机位置变化
      console.log("camera.position", camera_obj.position);
      renderer.render(scene, camera_obj); //执行渲染操作
    }); //监听鼠标、键盘事件
    return { renderer, camera_obj, mesh_obj };
  };
  let resize = function (renderer, camera, mesh) {
    window.onresize = function () {
      // 重置渲染器输出画布canvas尺寸
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 全屏情况下：设置观察范围长宽比aspect为窗口宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
      // 但是不会每渲染一帧，就通过相机的属性计算投影矩阵(节约计算资源)
      // 如果相机的一些属性发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      camera.updateProjectionMatrix();
    };
  };
  let stats = function (renderer, camera, mesh) {
    //创建stats对象
    const stats = new Stats();
    //stats.domElement:web页面上输出计算结果,一个div元素，
    document.body.appendChild(stats.domElement);
    // 渲染函数
    function render() {
      //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
      stats.update();
      renderer.render(scene, camera); //执行渲染操作
      requestAnimationFrame(render); //请求再次执行渲染函数render，渲染下一帧
    }
    render();
  };
  let arr = [
    //   ...Object.values(demo)
    ...Object.values(loader),
  ];
  let gui = function (renderer, camera, mesh) {
    // 实例化一个gui对象
    const gui = new GUI();
    gui.domElement.style.right = "0px";
    gui.domElement.style.width = "300px";
    //创建一个对象，对象属性的值可以被GUI库创建的交互界面改变
    const obj = { x: 2, y: 2, z: 2, color: 0x00ffff, scale: 0, bool: false };
    // 改变的obj属性数据类型是布尔值，交互界面是单选框
    gui.add(obj, "bool").name("是否旋转");
    gui
      .add(obj, "scale", [-100, 0, 100])
      .name("y坐标")
      .onChange(function (value) {
        mesh.position.y = value;
      });
    gui
      .add(obj, "scale", {
        left: -100,
        center: 0,
        right: 100,
        // 左: -100,//可以用中文
        // 中: 0,
        // 右: 100
      })
      .name("位置选择")
      .onChange(function (value) {
        mesh.position.x = value;
      });
    // setInterval(function () {
    //   camera.position.set(obj.x, obj.y, obj.z);
    // }, 10);
    // gui增加交互界面，用来改变obj对应属性
    gui.add(obj, "x", 0, 20);
    gui.add(obj, "y", 0, 20);
    gui.add(obj, "z", 0, 20);
    // .addColor()生成颜色值改变的交互界面
    gui.addColor(obj, "color").onChange(function (value) {
      mesh.material.color.set(value);
    });
    const ambient = new THREE.AmbientLight(0xffffff, 0.4);
    scene.add(ambient);
    // 光照强度属性.intensity
    console.log("ambient.intensity", ambient.intensity);
    // 通过GUI改变mesh.position对象的xyz属性
    gui.add(ambient, "intensity", 0, 2.0);

    gui
      .add(mesh.position, "x", 0, 180)
      .name("模型x轴")
      .step(10)
      .onChange(function (value) {
        //   mesh.position.x = value;
        // 你可以写任何你想跟着obj.x同步变化的代码
        // 比如mesh.position.y = value;
      });
    gui.add(mesh.position, "y", 0, 180).name("模型y轴").step(10);
    gui.add(mesh.position, "z", 0, 180).name("模型z轴").step(10);

    // 创建材质子菜单
    const matFolder = gui.addFolder("材质");
    matFolder.close();
    // 材质颜色color
    matFolder.addColor(obj, "color").onChange(function (value) {
      material.color.set(value);
    });
    // 材质高光颜色specular
    matFolder.addColor(obj, "specular").onChange(function (value) {
      material.specular.set(value);
    });
  };
  let plugins = [resize, stats, ...arr, gui];
  // 定义相机输出画布的尺寸(单位:像素px)
  // 创建渲染器对象
  let init = function () {
    // MeshLambertMaterial();
    const width = window.innerWidth; //窗口文档显示区的宽度作为画布宽度
    const height = window.innerHeight; //窗口文档显示区的高度作为画布高度
    let { renderer, camera_obj, mesh_obj } = render(width, height);
    // 设置相机控件轨道控制器OrbitControls
    document.getElementById("webgl").appendChild(renderer.domElement);
    const clock = new THREE.Clock();
    console.log("查看当前屏幕设备像素比", window.devicePixelRatio);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setClearColor(0x444444, 1); //设置背景颜色
    function animate() {
      //   const spt = clock.getDelta() * 1000; //毫秒
      renderer.render(scene, camera_obj); //执行渲染操作
      mesh_obj.rotateY(0.01); //每次绕y轴旋转0.01弧度
      requestAnimationFrame(animate); //请求再次执行渲染函数render，渲染下一帧
    }
    animate();
    plugins.map((res) => {
      res(renderer, camera_obj, mesh_obj, scene);
    });
  };
  init();
  // renderer 需要 scene 与 camera  scene新增add mesh  camera lookat 设置角度mesh
});
</script>

<style></style>
